import Flutter
import UIKit

public class SwiftImemberPrinterPlugin: NSObject, FlutterPlugin {
    
    // MARK: - Property
    private var viewController: UIViewController?
    
    // MARK: - Init
    init(viewController: UIViewController?) {
        super.init()
        self.viewController = viewController
    }
      
    // MARK: - Public_M
    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "com.feibi.imember_printer/imember_printer", binaryMessenger: registrar.messenger())
        
        let viewController = UIApplication.shared.delegate?.window??.rootViewController
        let instance = SwiftImemberPrinterPlugin(viewController: viewController)
        
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

  
    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        if call.method == "connectPrinter" {
            connectPrinter()
        } else if call.method == "printInventoryBill" {
            printInventoryBill(arguments: call.arguments, result: result)
        } else if call.method == "printInventoryAdjustment" {
            printInventoryAdjustment(arguments: call.arguments, result: result)
        } else if call.method == "printCheckoutBill" {
            printCheckoutBill(arguments: call.arguments, result: result)
        } else if call.method == "printTransactionDetails" {
            printTransactionDetails(arguments: call.arguments, result: result)
        } else if call.method == "printShippingLabel" {
            printShippingLabel(arguments: call.arguments, result: result)
        } else if call.method == "printPosImage" {
            printPosImage(arguments: call.arguments, result: result)
        } else if call.method == "printLabelImage" {
            printLabelImage(arguments: call.arguments, result: result)
        } else if call.method == "kickCashdrawer" {
            kickCashdrawer(arguments: call.arguments, result: result)
        } else {
            result(FlutterMethodNotImplemented)
        }
    }
    
    // MARK: - Private_M
    private func connectPrinter() {
        let printerConnectVc = BaseNavigationController(rootViewController: PrinterConnectViewController())
        printerConnectVc.modalPresentationStyle = .fullScreen
        viewController?.present(printerConnectVc, animated: true)
    }
    
    private func printInventoryBill(arguments: Any?, result: @escaping FlutterResult) {
        guard let products = arguments as? [[String: String?]] else {
            result(FlutterError.nilArgument)
            return
        }
        
        /// 頭部
        let printer = PosPrinter()
        printer.appendText("庫存盤點", justification: 1)
        printer.appendDateTime()
        printer.appendLines(2)
        
        /// 庫存
        printer.appendRow(columns: [
            TextColumn(text: "商品名稱", flex: 8),
            TextColumn(text: "數量", flex: 4, justification: 2)
        ])
        printer.appendDivide()
        products.forEach { product in
            let columns = [
                TextColumn(text: product["name"] ?? nil, flex: 8),
                TextColumn(text: product["number"] ?? nil, flex: 4, justification: 2)
            ]
            printer.appendRow(columns: columns)
        }

        printer.sendData(result: result)
        
    }
    
    private func printInventoryAdjustment(arguments: Any?, result: @escaping FlutterResult) {
        guard let products = arguments as? [[String: String?]] else {
            result(FlutterError.nilArgument)
            return
        }
        
        /// 頭部
        let printer = PosPrinter()
        printer.appendText("盤點調整", justification: 1)
        printer.appendDateTime()
        printer.appendLines(2)
        
        /// 庫存盤點
        products.forEach { product in
            printer.appendText("商品名稱：\(product["name"] as? String ?? "")")
            printer.appendText("與實際盤點不符：\(product["differenceNumber"] as? String ?? "")")
            printer.appendText("原因：\(product["differenceReason"] as? String ?? "")")
            printer.appendDivide()
        }
        
        printer.sendData(result: result)
    }
    
    private func printCheckoutBill(arguments: Any?, result: @escaping FlutterResult) {        
        let printer = PosPrinter()
        /// 頭部
        printer.appendText("小結單", justification: 1)
        printer.appendDateTime()
        printer.appendLines(1)
        printer.appendText("小結營業額：20000", justification: 1)
        
        /// 現金
        printer.appendTitle("現金")
        printer.appendRow(columns: [
            TextColumn(text: "實點金額", flex: 8),
            TextColumn(text: "7000", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "應有金額", flex: 8),
            TextColumn(text: "7000", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "臨時支出", flex: 8),
            TextColumn(text: "100", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "短溢金額", flex: 8),
            TextColumn(text: "0", flex: 4, justification: 2)
        ])
        printer.appendText("短溢原因：快遞延誤")
        printer.appendDivide()
        printer.appendText("臨時支出", justification: 1)
        printer.appendText("金額：50")
        printer.appendText("事由：快遞到付")
        printer.appendText("金額：50")
        printer.appendText("事由：快遞到付")
        
        /// 信用卡
        printer.appendTitle("信用卡")
        printer.appendRow(columns: [
            TextColumn(text: "實點金額", flex: 8),
            TextColumn(text: "5000", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "應有金額", flex: 8),
            TextColumn(text: "5000", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "短溢金額", flex: 8),
            TextColumn(text: "0", flex: 4, justification: 2)
        ])
        printer.appendText("短溢原因：快遞延誤")
        
        /// 行動支付
        printer.appendTitle("行動支付")
        printer.appendRow(columns: [
            TextColumn(text: "LINE Pay金額", flex: 8),
            TextColumn(text: "2500", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "街口支付金額", flex: 8),
            TextColumn(text: "2500", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "微信支付金額", flex: 8),
            TextColumn(text: "0", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "支付寶金額", flex: 8),
            TextColumn(text: "0", flex: 4, justification: 2)
        ])
        printer.appendRow(columns: [
            TextColumn(text: "其他支付金額", flex: 8),
            TextColumn(text: "0", flex: 4, justification: 2)
        ])
        
        /// 尾部
        printer.appendLines(1)
        printer.appendDivide()
        printer.appendText("現金留存金額：5000", justification: 1)
        printer.appendText("現金匯出金額：7000", justification: 1)
        printer.appendDivide()
        
        printer.sendData(result: result)
    }
    
    private func printTransactionDetails(arguments: Any?, result: @escaping FlutterResult) {
        guard let products = arguments as? [[String: String?]] else {
            result(FlutterError.nilArgument)
            return
        }
        
        /// 頭部
        let printer = PosPrinter()
        printer.appendText("訂單編號-交易明細", justification: 1)
        printer.appendDateTime()
        printer.appendLines(1)
        
        /// 商品明細
        printer.appendRow(columns: [
            TextColumn(text: "品名", flex: 6, justification: 0),
            TextColumn(text: "數量", flex: 2, justification: 1),
            TextColumn(text: "單價", flex: 2, justification: 1),
            TextColumn(text: "金額", flex: 2, justification: 2),
        ])
        printer.appendDivide()
        var totalPrice: Float = 0
        products.forEach { product in
            let number = product["number"] ?? nil
            let unitPrice = product["price"] ?? nil
            let price = (Float(number ?? "") ?? 0.0) * (Float(unitPrice ?? "") ?? 0.0)
            totalPrice += price
            let columns = [
                TextColumn(text: product["name"] ?? nil, flex: 6, justification: 0),
                TextColumn(text: number, flex: 2, justification: 1),
                TextColumn(text: unitPrice, flex: 2, justification: 1),
                TextColumn(text: String(price), flex: 2, justification: 2),
            ]
            printer.appendRow(columns: columns)
        }
        printer.appendDivide()
        printer.appendText("總計：$\(totalPrice)", justification: 2)

        /// 好康推薦
        printer.appendLines(2)
        printer.appendTitle("好康推薦")
        printer.appendQRCode("https://www.baidu.com")
        printer.appendLines(1)
        printer.appendText("全場商品消費滿5000元折200元", justification: 1)
        printer.appendLines(2)
        printer.appendText("活動詳情")
        printer.appendText("1.商品以店內公告為準，數量有限售完為止")
        printer.appendText("2.線上購物不計算此優惠")
        printer.appendLines(2)
        printer.appendText("注意事項")
        printer.appendText("1.本商品不得與其他優惠併用")
        printer.appendText("2.再次交易才可使用此優惠券")
        printer.appendLines(2)
        printer.appendTitle("活動期限：2021/12/31止")
        
        printer.sendData(result: result)
    }
    
    private func printShippingLabel(arguments: Any?, result: @escaping FlutterResult) {
        let printer = LabelPrinter()
        printer.printShippingLabel(result: result)
    }

    private func printLabelImage(arguments: Any?, result: @escaping FlutterResult) {
        guard let imageData = arguments as? Data else {
            result(FlutterError.nilArgument)
            return
        }
        
        let printer = LabelPrinter()
        printer.appendImage(imageData)
        printer.sendData(result: result)
    }

    private func printPosImage(arguments: Any?, result: @escaping FlutterResult) {
        guard let imageData = arguments as? Data else {
            result(FlutterError.nilArgument)
            return
        }
        
        let printer = PosPrinter()
        printer.appendImage(imageData)
        printer.sendData(result: result)
    }
    
    private func kickCashdrawer(arguments: Any?, result: @escaping FlutterResult) {
        guard let number = arguments as? Int else {
            result(FlutterError.nilArgument)
            return
        }
        
        let printer = PosPrinter()
        printer.kickCashDrawer(number: number)
        printer.sendData(result: result)
    }
}

extension FlutterError {
    static let nilArgument = FlutterError(
        code: "argument.nil",
        message: "Expect an argument when invoking channel method, but it is nil.",
        details: nil
    )
    
    static func failedArgumentField<T>(_ fieldName: String, type: T.Type) -> FlutterError {
        return FlutterError(
            code: "argument.failedField",
            message: "Expect a `\(fieldName)` field with type <\(type)> in the argument, " +
                     "but it is missing or type not matched.",
            details: nil
        )
    }
    
    static func customizeFailedMessage(_ message: String) -> FlutterError {
        return FlutterError(
            code: "customize.failed",
            message: message,
            details: nil
        )
    }
}
